# Subversion Info: $Id: rich_text.py 1577 2010-06-28 15:01:11Z millsks $

from PyQt4.QtCore import QBuffer, QByteArray, QIODevice, QSize, Qt, QUrl, QVariant, SIGNAL
from PyQt4.QtGui import QApplication, QCheckBox, QColor, QColorDialog, QComboBox, \
                        QDialog, QFileDialog, QFont, QFontComboBox, QGridLayout, \
                        QGroupBox, QIcon, QImageReader, QLabel, QLayout, QMainWindow, \
                        QMessageBox, QPixmap, QPushButton, QSizePolicy, QSpinBox, \
                        QTextCharFormat, QTextCursor, QTextDocument, QTextEdit, \
                        QTextImageFormat, QTextListFormat, QToolBar, QWidget

import Ft.Lib
import amara
import os

class RichTextEdit(QMainWindow):
    def __init__(self, data='', parent=None):
        super(RichTextEdit, self).__init__(parent)
        
        bgcolor_filename = os.path.join(os.getenv('FTDBRESDIR'), 'img', 
                                        'background-color.svg')
        self._bgcolor_svg = amara.parse(source=open(bgcolor_filename, 'rb').read(), 
                                         prefixes={u'default':u'http://www.w3.org/2000/svg'},
                                         rules=[amara.binderytools.ws_strip_element_rule('/*')]).svg
                                         
        fgcolor_filename = os.path.join(os.getenv('FTDBRESDIR'), 'img', 
                                        'text-color.svg')
        self._fgcolor_svg = amara.parse(source=open(fgcolor_filename, 'rb').read(), 
                                         prefixes={u'default':u'http://www.w3.org/2000/svg'},
                                         rules=[amara.binderytools.ws_strip_element_rule('/*')]).svg
        
        self._current_bgcolor = QColor(255,255,255)
        
        self.tbrFormatToolbar = QToolBar()
        self.txtEditor = QTextEdit(data)
        self.cboFontList = QFontComboBox()
        self.cboFontSizes = QComboBox()
        
        self.pnlTextFormatting = QWidget()
        self.lm_TextFormatting = QGridLayout()
        self.btnFormatBold = QPushButton()
        self.btnFormatItalic = QPushButton()
        self.btnFormatUnderline = QPushButton()
    
        self.pnlColorFormatting = QWidget()
        self.lm_ColorFormatting = QGridLayout()
        self.btnFormatBGColor = QPushButton()
        self.btnFormatFGColor = QPushButton()
        
        # TODO: Finish developing the rest of the toolbar
#        self.pnlTextAlignmentFormatting = QWidget()
#        self.lm_TextAlignmentFormatting = QGridLayout()
#        self.btnFormatLeftJustify = QPushButton()
#        self.btnFormatCenterJustify = QPushButton()
#        self.btnFormatRightJustify = QPushButton()
#        self.btnFormatFillJustify = QPushButton() 
#        self.btnFormatEnumList = QPushButton()
#        self.btnFormatBulletList = QPushButton()
#        self.btnFormatIndentMore = QPushButton()
#        self.btnFormatIndentLess = QPushButton()
#        
#        self.pnlInsertObject = QWidget()
#        self.lm_InsertObject = QGridLayout()
#        self.btnInsertImage = QPushButton()
#        self.btnInsertHyperlink = QPushButton()
                
        self._initToolbar()
        self._initGui()
    
    def _initToolbar(self):
        self._initTextFormattingSection()
        self._initColorFormattingSection()
#        self._initTextAlignmentFormattingSection()
#        self._initInsertObjectSection()
        
        self.cboFontList.setMaximumWidth(200)
        
        self.cboFontSizes.addItems([str(i) for i in range(8,121)])
        self.cboFontSizes.setEditable(True)
        
        spacer_widget = QWidget()
        spacer_widget.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
                                                QSizePolicy.Fixed))        

        self.tbrFormatToolbar.addWidget(self.pnlTextFormatting)
        self.tbrFormatToolbar.addSeparator()
        
        self.tbrFormatToolbar.addWidget(self.pnlColorFormatting)
        self.tbrFormatToolbar.addSeparator()
        
#        self.tbrFormatToolbar.addWidget(self.pnlInsertObject)
#        self.tbrFormatToolbar.addSeparator()
#        
#        self.tbrFormatToolbar.addWidget(self.pnlTextAlignmentFormatting)
#        self.tbrFormatToolbar.addSeparator()
#
#        self.tbrFormatToolbar.addWidget(self.cboFontList)
#        self.tbrFormatToolbar.addWidget(self.cboFontSizes)
#        self.tbrFormatToolbar.addSeparator()
        
        self.tbrFormatToolbar.addWidget(spacer_widget)
        
        self.addToolBar(Qt.TopToolBarArea, self.tbrFormatToolbar)
        
        # Disabled until formatting procedures are integrated
#        self.pnlTextAlignmentFormatting.setDisabled(True)
        
#        self.connect(self.cboFontList, SIGNAL('currentIndexChanged(int)'), self._updateFontFamily)
#        self.connect(self.cboFontSizes, SIGNAL('currentIndexChanged(int)'), self._updateFontFamily)
    
    def _initTextFormattingSection(self):
        bold_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
                                      'text-bold.png'))
        italic_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
                                        'text-italic.png'))
        underline_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
                                           'text-underline.png'))
        
        self.btnFormatBold.setIcon(bold_ico)
        self.btnFormatBold.setFlat(True)
        self.btnFormatBold.setMaximumSize(22,22)
        self.btnFormatBold.setToolTip('Bold')
        
        self.btnFormatItalic.setIcon(italic_ico)
        self.btnFormatItalic.setFlat(True)
        self.btnFormatItalic.setMaximumSize(22,22)
        self.btnFormatItalic.setToolTip('Italic')
        
        self.btnFormatUnderline.setIcon(underline_ico)
        self.btnFormatUnderline.setFlat(True)
        self.btnFormatUnderline.setMaximumSize(22,22)
        self.btnFormatUnderline.setToolTip('Underline')
        
        self.lm_TextFormatting.addWidget(self.btnFormatBold, 0,0)
        self.lm_TextFormatting.addWidget(self.btnFormatItalic, 0,1)
        self.lm_TextFormatting.addWidget(self.btnFormatUnderline, 0,2)
        
        self.lm_TextFormatting.setHorizontalSpacing(2)
        self.lm_TextFormatting.setMargin(0)
        
        self.pnlTextFormatting.setLayout(self.lm_TextFormatting)
    
        for child in self.pnlTextFormatting.children():
            if isinstance(child, QPushButton):
                child.setCheckable(True)
        
        self.connect(self.btnFormatBold, SIGNAL('toggled(bool)'), self._updateFontWeight)
        self.connect(self.btnFormatItalic, SIGNAL('toggled(bool)'), self._updateFontItalics)
        self.connect(self.btnFormatUnderline, SIGNAL('toggled(bool)'), self._updateFontUnderline)
                
    def _initColorFormattingSection(self):                    
        bgcolor_byte_array = QByteArray(self._bgcolor_svg.xml(indent=True))
        bgcolor_pixmap = QPixmap()
        bgcolor_pixmap.loadFromData(bgcolor_byte_array, 'SVG')
        bgcolor_ico = QIcon(bgcolor_pixmap)       
        
        fgcolor_byte_array = QByteArray(self._fgcolor_svg.xml(indent=True))
        fgcolor_pixmap = QPixmap()
        fgcolor_pixmap.loadFromData(fgcolor_byte_array, 'SVG')
        fgcolor_ico = QIcon(fgcolor_pixmap)
        
        self.btnFormatBGColor.setIcon(bgcolor_ico)
        self.btnFormatBGColor.setFlat(True)
        self.btnFormatBGColor.setMaximumSize(22,22)
        self.btnFormatBGColor.setToolTip('Background Color')
        
        self.btnFormatFGColor.setIcon(fgcolor_ico)
        self.btnFormatFGColor.setFlat(True)
        self.btnFormatFGColor.setMaximumSize(22,22)
        self.btnFormatFGColor.setToolTip('Text Color')
        
        self.lm_ColorFormatting.addWidget(self.btnFormatBGColor, 0,0)
        self.lm_ColorFormatting.addWidget(self.btnFormatFGColor, 0,1)
        
        self.lm_ColorFormatting.setHorizontalSpacing(2)
        self.lm_ColorFormatting.setMargin(0)
        
        self.pnlColorFormatting.setLayout(self.lm_ColorFormatting)
        
        self.connect(self.btnFormatBGColor, SIGNAL('clicked()'), self._setBGColor)
        self.connect(self.btnFormatFGColor, SIGNAL('clicked()'), self._setFGColor)
    
#    def _initTextAlignmentFormattingSection(self):
#        left_justify_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                              'format-justify-left.png'))
#        center_justify_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                                'format-justify-center.png'))
#        right_justify_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                               'format-justify-right.png'))
#        fill_justify_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                              'format-justify-fill.png'))
#        enum_list_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                           'enum-list.png'))
#        bullet_list_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                             'bullet-list.png'))
#        indent_more_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                             'format-indent-more.png'))
#        indent_less_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                             'format-indent-less.png'))
#        
#        self.btnFormatLeftJustify.setIcon(left_justify_ico)
#        self.btnFormatLeftJustify.setFlat(True)
#        self.btnFormatLeftJustify.setMaximumSize(22,22)
#        self.btnFormatLeftJustify.setToolTip('Text Align: Left')
#        
#        self.btnFormatCenterJustify.setIcon(center_justify_ico)
#        self.btnFormatCenterJustify.setFlat(True)
#        self.btnFormatCenterJustify.setMaximumSize(22,22)
#        self.btnFormatCenterJustify.setToolTip('Text Align: Center')
#        
#        self.btnFormatRightJustify.setIcon(right_justify_ico)
#        self.btnFormatRightJustify.setFlat(True)
#        self.btnFormatRightJustify.setMaximumSize(22,22)
#        self.btnFormatRightJustify.setToolTip('Text Align: Right')
#        
#        self.btnFormatFillJustify.setIcon(fill_justify_ico)
#        self.btnFormatFillJustify.setFlat(True)
#        self.btnFormatFillJustify.setMaximumSize(22,22)
#        self.btnFormatFillJustify.setToolTip('Text Align: Justify')
#        
#        self.btnFormatEnumList.setIcon(enum_list_ico)
#        self.btnFormatEnumList.setFlat(True)
#        self.btnFormatEnumList.setMaximumSize(22,22)
#        self.btnFormatEnumList.setToolTip('Insert Enumerated List')
#        
#        self.btnFormatBulletList.setIcon(bullet_list_ico)
#        self.btnFormatBulletList.setFlat(True)
#        self.btnFormatBulletList.setMaximumSize(22,22)
#        self.btnFormatBulletList.setToolTip('Insert Bullet List')
#        
#        self.btnFormatIndentMore.setIcon(indent_more_ico)
#        self.btnFormatIndentMore.setFlat(True)
#        self.btnFormatIndentMore.setMaximumSize(22,22)
#        self.btnFormatIndentMore.setToolTip('Indent More')
#        
#        self.btnFormatIndentLess.setIcon(indent_less_ico)
#        self.btnFormatIndentLess.setFlat(True)
#        self.btnFormatIndentLess.setMaximumSize(22,22)
#        self.btnFormatIndentLess.setToolTip('Indent Less')
#        
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatLeftJustify, 
#                                                  0,0)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatCenterJustify,
#                                                  0,1)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatRightJustify,
#                                                  0,2)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatFillJustify,
#                                                  0,3)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatEnumList, 0,4)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatBulletList, 0,5)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatIndentMore, 0,6)
#        self.lm_TextAlignmentFormatting.addWidget(self.btnFormatIndentLess, 0,7)
#        
#        self.lm_TextAlignmentFormatting.setHorizontalSpacing(2)
#        self.lm_TextAlignmentFormatting.setMargin(0)
#        
#        self.pnlTextAlignmentFormatting.setLayout(self.lm_TextAlignmentFormatting)
#        
#        for child in self.pnlTextAlignmentFormatting.children():
#            if isinstance(child, QPushButton):
#                child.setCheckable(True)
#        
#        self.connect(self.btnFormatEnumList, SIGNAL('toggled(bool)'), self._insertNumericList)
#        self.connect(self.btnFormatBulletList, SIGNAL('toggled(bool)'), self._insertBulletedList)
#                
#    def _initInsertObjectSection(self):
#        insert_image_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                              'insert-image.png'))
#        insert_link_ico = QIcon(os.path.join(os.getenv('FTDBRESDIR'), 'img', 
#                                             'insert-hyperlink.png'))
#        
#        self.btnInsertImage.setIcon(insert_image_ico)
#        self.btnInsertImage.setFlat(True)
#        self.btnInsertImage.setMaximumSize(22,22)
#        self.btnInsertImage.setToolTip('Insert image...')
#        
#        self.btnInsertHyperlink.setIcon(insert_link_ico)
#        self.btnInsertHyperlink.setFlat(True)
#        self.btnInsertHyperlink.setMaximumSize(22,22)
#        self.btnInsertHyperlink.setToolTip('Insert hyperlink...')
#        
#        self.lm_InsertObject.addWidget(self.btnInsertImage, 0,0)
#        self.lm_InsertObject.addWidget(self.btnInsertHyperlink, 0,1)
#        
#        self.lm_InsertObject.setHorizontalSpacing(2)
#        self.lm_InsertObject.setMargin(0)
#        
#        self.pnlInsertObject.setLayout(self.lm_InsertObject)
#        
#        self.connect(self.btnInsertImage, SIGNAL('clicked()'), self._insertImage)
        
    def _initGui(self):
        self.setCentralWidget(self.txtEditor)
        self.connect(self.txtEditor, SIGNAL('currentCharFormatChanged(const QTextCharFormat&)'), self._currentCharFormatChanged)
        
    def _currentCharFormatChanged(self, text_format):
        self.cboFontList.blockSignals(True)
        self.cboFontSizes.blockSignals(True)
        self.btnFormatBold.blockSignals(True)
        self.btnFormatItalic.blockSignals(True)
        self.btnFormatUnderline.blockSignals(True)
        
        self.cboFontList.setCurrentIndex(self.cboFontList.findText(text_format.fontFamily()))
        self.cboFontSizes.setCurrentIndex(self.cboFontSizes.findText(str(int(text_format.fontPointSize()))))
        self.btnFormatBold.setChecked(text_format.fontWeight() == QFont.Bold)
        self.btnFormatItalic.setChecked(text_format.fontItalic())
        self.btnFormatUnderline.setChecked(text_format.fontUnderline())
        
        self.btnFormatBGColor.setIcon(self._updateBGIcon(text_format))
        self.btnFormatFGColor.setIcon(self._updateFGIcon(text_format))
        
        self.cboFontList.blockSignals(False)
        self.cboFontSizes.blockSignals(False)
        self.btnFormatBold.blockSignals(False)
        self.btnFormatItalic.blockSignals(False)
        self.btnFormatUnderline.blockSignals(False)    

    def _mergeFormatOnWordOrSelection(self, text_format):
        self.cursor = self.txtEditor.textCursor()
        
        if not self.cursor.hasSelection():
            self.cursor.select(QTextCursor.WordUnderCursor)
        
        self.cursor.mergeCharFormat(text_format)
        self.txtEditor.mergeCurrentCharFormat(text_format)
        
    def _updateFontFamily(self):        
        font_name = self.cboFontList.currentText()
        font_point_size = int(self.cboFontSizes.currentText() \
                              if self.cboFontSizes.currentText() != '' else '8')
        is_bold = self.btnFormatBold.isChecked()
        is_italic = self.btnFormatItalic.isChecked()
        is_underline = self.btnFormatUnderline.isChecked()
        text_format = QTextCharFormat()        
        font = QFont(font_name, font_point_size)
        
        text_format.setFont(font)
        text_format.setFontWeight(is_bold)
        text_format.setFontItalic(is_italic)
        text_format.setFontUnderline(is_underline)
        
        self._mergeFormatOnWordOrSelection(text_format)
        
        self.cboFontList.lineEdit().setCursorPosition(0)
        self.tbrFormatToolbar.setFocus(Qt.OtherFocusReason)
    
    def _updateFontWeight(self, state):
        text_format = QTextCharFormat()
        text_format.setFontWeight(QFont.Bold if state else QFont.Normal)
        
        self._mergeFormatOnWordOrSelection(text_format)
        
    def _updateFontItalics(self, state):
        text_format = QTextCharFormat()
        text_format.setFontItalic(state)
        
        self._mergeFormatOnWordOrSelection(text_format)
            
    def _updateFontUnderline(self, state):
        text_format = QTextCharFormat()
        text_format.setFontUnderline(state)
        
        self._mergeFormatOnWordOrSelection(text_format)
    
    def _changeFillColor(self, icon_svg, rgb_color):
        r = rgb_color.red()
        g = rgb_color.green()
        b = rgb_color.blue()            
        hex_color = '%02x%02x%02x' % (r,g,b)
        icon_style = str(icon_svg.g[0].rect[0].style).split(';')
        style_attributes = dict([sa.split(':') for sa in icon_style])
        style_attributes['fill'] = '#%s' % hex_color
        style_attributes = style_attributes.items()
        icon_svg.g[0].rect[0].style = u';'.join('%s:%s' % (k,v) \
                                                for k,v in style_attributes)
    
    def _setBodyBGColor(self, rgb_color, html_xml):
        r = rgb_color.red()
        g = rgb_color.green()
        b = rgb_color.blue()            
        hex_color = '%02x%02x%02x' % (r,g,b)
        body_style = str(html_xml.body[0].style)[:-1].split('; ')
        style_attributes = dict([sa.split(':') for sa in body_style])
        style_attributes['background-color'] = '#%s' % hex_color
        style_attributes = style_attributes.items()
        html_xml.body[0].style = u';'.join('%s:%s' % (k,v) \
                                                for k,v in style_attributes)
                        
    def _setBGColor(self):
        text_cursor = self.txtEditor.textCursor()
        text_format = QTextCharFormat()
        selected_color = QColorDialog.getColor()
        
        if selected_color:            
            self._changeFillColor(self._bgcolor_svg, selected_color)
       
        bgcolor_byte_array = QByteArray(self._bgcolor_svg.xml(indent=True))
        bgcolor_pixmap = QPixmap()
        bgcolor_pixmap.loadFromData(bgcolor_byte_array, 'SVG')
        bgcolor_ico = QIcon(bgcolor_pixmap)
            
        self.btnFormatBGColor.setIcon(bgcolor_ico)
        text_format.setBackground(selected_color)
        
        if not text_cursor.hasSelection():
            self._current_bgcolor = selected_color
            
            rules=[amara.binderytools.ws_strip_element_rule('/*')]
            html_xml = amara.parse(self.txtEditor.toHtml(), rules=rules).html
        
            html_xml.head[0].xml_remove_child(html_xml.head[0].style[0])
        
            self._setBodyBGColor(self._current_bgcolor, html_xml)
            
            self.txtEditor.setHtml(html_xml.xml(indent=True))
                                       
        self._mergeFormatOnWordOrSelection(text_format)
        
    def _setFGColor(self):
        text_format = QTextCharFormat()
        selected_color = QColorDialog.getColor()
        
        if selected_color:            
            self._changeFillColor(self._fgcolor_svg, selected_color)
       
        fgcolor_byte_array = QByteArray(self._fgcolor_svg.xml(indent=True))
        fgcolor_pixmap = QPixmap()
        fgcolor_pixmap.loadFromData(fgcolor_byte_array, 'SVG')
        fgcolor_ico = QIcon(fgcolor_pixmap)
        
        self.btnFormatFGColor.setIcon(fgcolor_ico)
        text_format.setForeground(selected_color)
        
        self._mergeFormatOnWordOrSelection(text_format)
    
    def _updateFGIcon(self, text_format):
        self._changeFillColor(self._fgcolor_svg, text_format.foreground().color())
       
        fgcolor_byte_array = QByteArray(self._fgcolor_svg.xml(indent=True))
        fgcolor_pixmap = QPixmap()
        fgcolor_pixmap.loadFromData(fgcolor_byte_array, 'SVG')
        fgcolor_ico = QIcon(fgcolor_pixmap)
        
        return fgcolor_ico
    
    def _updateBGIcon(self, text_format):
        self._changeFillColor(self._bgcolor_svg, text_format.background().color())
       
        bgcolor_byte_array = QByteArray(self._bgcolor_svg.xml(indent=True))
        bgcolor_pixmap = QPixmap()
        bgcolor_pixmap.loadFromData(bgcolor_byte_array, 'SVG')
        bgcolor_ico = QIcon(bgcolor_pixmap)
        
        return bgcolor_ico
    
    def _insertNumericList(self):
        text_cursor = self.txtEditor.textCursor()
        list_format = QTextListFormat()
        
        list_format.setStyle(QTextListFormat.ListDecimal)
        text_cursor.createList(list_format)
        
    def _insertBulletedList(self):
        text_cursor = self.txtEditor.textCursor()
        list_format = QTextListFormat()
        
        list_format.setStyle(QTextListFormat.ListDisc)
        text_cursor.createList(list_format)
        
    def _insertImage(self):
        image_filename = QFileDialog.getOpenFileName(self, 'Select image...', os.getenv('HOME'), 'Images (*.bmp *.gif *.jpg *.png)')
        
        if image_filename != '':    
            image_url = QUrl.fromLocalFile(image_filename)
            image = QImageReader(image_filename).read()
            image_format = QTextImageFormat()

            self.txtEditor.document().addResource(QTextDocument.ImageResource, 
                                                  image_url, QVariant(image))
            
            result = QMessageBox.question(self, 'Image Size', 'Would you like to keep the original size of the image or scale the dimensions?\n\nOriginal image dimensions: %sx%s\n\nYes: Scale\n No: Keep original size' % (image.width(), image.height()), QMessageBox.Yes|QMessageBox.No, QMessageBox.No)
            
            if result == QMessageBox.No:
                image_format.setWidth(image.width())
                image_format.setHeight(image.height())
            else:
                scaled_size = ScaleImageInput.getNewDimensions(image.size())
                
                image_format.setWidth(scaled_size.width())
                image_format.setHeight(scaled_size.height())
            
            image_format.setName(image_url.toString())
            self.txtEditor.textCursor().insertImage(image_format)
            self.txtEditor.setLineWrapColumnOrWidth(self.txtEditor.lineWrapColumnOrWidth())
    
    def editor(self):
        return self.txtEditor
    
    def toPlainText(self):
        return self.txtEditor.toPlainText()
    
    def setPlainText(self, data):
        self.txtEditor.setPlainText(data)
          
    def data(self):
        if self.txtEditor.toPlainText() == '':
            return ''
        
        rules=[amara.binderytools.ws_strip_element_rule('/*')]
        html_xml = amara.parse(str(self.txtEditor.toHtml()), rules=rules).html
        
        html_xml.head[0].xml_remove_child(html_xml.head[0].style[0])
        
        self._setBodyBGColor(self._current_bgcolor, html_xml)
#        self._convertImagesToBase64(html_xml)
        
        return html_xml.xml(indent=True)
    
    def setData(self, data):
        try:
            html_xml = amara.parse(str(data), rules=[amara.binderytools.ws_strip_element_rule('/*')]).html
            
            if hasattr(html_xml.head[0], 'style'):
                html_xml.head[0].xml_remove_child(html_xml.head[0].style[0])
            
            for e_image in html_xml.xml_xpath('//img'):
                image_url = QUrl(e_image.src)
                image = QImageReader(image_url.toLocalFile()).read()
    
                self.txtEditor.document().addResource(QTextDocument.ImageResource, 
                                                      image_url, QVariant(image))
            
            
#            for e_image in html_xml.xml_xpath('//img'):
#                # convert image embed data
#                image_src = str(e_image.src)
#                re = QRegExp("data:([a-zA-Z]+/[a-zA-Z]+);base64,([a-zA-Z0-9/=+]+)*")
#    
#                ba = QByteArray.fromBase64(QUrl.fromPercentEncoding(re.cap(2).toAscii()).toAscii())
#                image = QImage()
#                image.loadFromData( ba )
#                url = QString(QCryptographicHash.hash(ba, QCryptographicHash.Sha1))
#                url = "srcdata" + url
#                print unicode(url, "iso-8859-1").encode("iso-8859-1")
#                self.txtEditor.document().addResource(QTextDocument.ImageResource, QUrl(url), image)
#                e_image.src = unicode(url,'iso-8859-1').encode('iso-8859-1')
#            
            self.txtEditor.setHtml(str(html_xml.xml(indent=True)).replace('<body style=" font-family:\'Sans\'; font-size:8pt; font-weight:400; font-style:normal;">', '<body>'))                                   
        except Ft.Lib.UriException:
            self.txtEditor.setPlainText(str(data))
    
    def clear(self):
        self.txtEditor.clear()
                 
    def _convertImagesToBase64(self, html_xml):
        for e_image in html_xml.xml_xpath('//img'):
            image_url = QUrl(e_image.src)
            image = QImageReader(image_url.toLocalFile()).read()
            
            byte_array = QByteArray()
            buffer = QBuffer(byte_array)
            buffer.open(QIODevice.WriteOnly)
            image.save(buffer, 'PNG') 
            
            e_image.src = unicode('data:image/png;base64,%s' % byte_array.toBase64())
            
    def closeEvent(self, evt):
        self.emit(SIGNAL('store_data'))
        

class ScaleImageInput:
    @classmethod
    def getNewDimensions(cls, original_size=QSize(0,0), parent=None):
        dialog = ScaleImageInputDialog(original_size, parent)
        
        if dialog.result() == QDialog.Accepted:
            return dialog.data()

class ScaleImageInputDialog(QDialog):
    def __init__(self, original_size=QSize(0,0), parent=None):
        super(ScaleImageInputDialog, self).__init__(parent)
        
        self._original_size = original_size
        self._current_size = QSize(self._original_size.width(), 
                                    self._original_size.height())
        
        self.lm_ScaleImageInput = QGridLayout()
        
        self.grpImageSize = QGroupBox('Image Size')
        self.lm_ImageSize = QGridLayout()
        self.lblScaleWidth = QLabel('Width:')
        self.spnScaleWidthPixel = QSpinBox()
        self.lblScaleHeight = QLabel('Height:')
        self.spnScaleHeightPixel = QSpinBox()
        self.chkKeepRatio = QCheckBox('Keep ratio proportionally linked')
        
        self.btnReset = QPushButton('Reset')
        self.btnCancel = QPushButton('Cancel')
        self.btnScale = QPushButton('Scale')
        
        self._initPalettes()
        self._initGui()
        
        self.setWindowTitle('Scale image')
        
        self.activateWindow()
        self.raise_()
        self.exec_()
    
    def _initPalettes(self):
        self._headerFont = QFont('', QApplication.font().pointSize()+2,
                                  QFont.Bold, False)
        
        self._defaultFont = QFont('', QApplication.font().pointSize(),
                                  QFont.Normal, False)
    
    def _initGui(self):        
        self.spnScaleWidthPixel.setRange(0,9999)
        self.spnScaleWidthPixel.setValue(self._current_size.width())
        
        self.spnScaleHeightPixel.setRange(0,9999)
        self.spnScaleHeightPixel.setValue(self._current_size.height())
        
        self.lm_ImageSize.addWidget(self.lblScaleWidth, 0,0)
        self.lm_ImageSize.addWidget(self.spnScaleWidthPixel, 0,1)
        self.lm_ImageSize.addWidget(self.lblScaleHeight, 1,0)
        self.lm_ImageSize.addWidget(self.spnScaleHeightPixel, 1,1)
        self.lm_ImageSize.addWidget(self.chkKeepRatio, 0,3,2,1, Qt.AlignCenter)
        
        self.lm_ImageSize.setColumnMinimumWidth(2,10)
        
        self.grpImageSize.setLayout(self.lm_ImageSize)
        self.grpImageSize.setFont(self._headerFont)
        
        for child in self.grpImageSize.children():
            if not isinstance(child, QLayout):
                child.setFont(self._defaultFont)
                
        self.lm_ScaleImageInput.addWidget(self.grpImageSize, 0,0,1,4)
        self.lm_ScaleImageInput.addWidget(self.btnReset, 1,1)
        self.lm_ScaleImageInput.addWidget(self.btnCancel, 1,2)
        self.lm_ScaleImageInput.addWidget(self.btnScale, 1,3)
        
        self.setLayout(self.lm_ScaleImageInput)
    
        self.connect(self.spnScaleWidthPixel.lineEdit(), SIGNAL('editingFinished()'), self._updatePixelHeight)
        self.connect(self.spnScaleHeightPixel.lineEdit(), SIGNAL('editingFinished()'), self._updatePixelWidth)
        self.connect(self.btnReset, SIGNAL('clicked()'), self._reset)
        self.connect(self.btnCancel, SIGNAL('clicked()'), self.reject)
        self.connect(self.btnScale, SIGNAL('clicked()'), self.accept)
    
    def _updatePixelHeight(self):
        new_width = self.spnScaleWidthPixel.value()
        
        if new_width != self._current_size.width():            
            if self.chkKeepRatio.isChecked():
                size_ratio = self._current_size.width()/float(self._current_size.height())
                                
                self.spnScaleHeightPixel.blockSignals(True)
                self.spnScaleHeightPixel.setValue(int(round(new_width/size_ratio)))
                self.spnScaleHeightPixel.blockSignals(False)
            
                self._current_size.setHeight(int(round(new_width/size_ratio)))
        
        self._current_size.setWidth(new_width)
        
    def _updatePixelWidth(self):
        new_height = self.spnScaleHeightPixel.value()
        
        if new_height != self._current_size.height():            
            if self.chkKeepRatio.isChecked():
                size_ratio = self._current_size.height()/float(self.width())
                        
                self.spnScaleWidthPixel.blockSignals(True) 
                self.spnScaleWidthPixel.setValue(int(round(new_height/size_ratio)))
                self.spnScaleWidthPixel.blockSignals(False)
            
                self._current_size.setWidth(int(round(new_height/size_ratio)))
        
        self._current_size.setHeight(new_height)
    
    def _reset(self):
        self.spnScaleWidthPixel.blockSignals(True)
        self.spnScaleHeightPixel.blockSignals(True)
        
        self.spnScaleWidthPixel.setValue(self._original_size.width())
        self.spnScaleHeightPixel.setValue(self._original_size.height())
        
        self.spnScaleWidthPixel.blockSignals(False)
        self.spnScaleHeightPixel.blockSignals(False)
    
    def data(self):
        return self._current_size

## ----------------------------------------------------------------------------
def main():
    from ftdb import env
    env.initFTDBEnvironment()
    
    app = QApplication([])
    
    rtfEditor = RichTextEdit()
    rtfEditor.show()
    rtfEditor.raise_()
    
    rtfEditor.setData(QByteArray.fromBase64('PGh0bWw+CiAgPGhlYWQ+CiAgICA8bWV0YSBjb250ZW50PSIxIiBuYW1lPSJxcmljaHRleHQiLz4KICA8L2hlYWQ+CiAgPGJvZHkgc3R5bGU9ImZvbnQtc2l6ZToxM3B0O2ZvbnQtd2VpZ2h0OjQwMDsgZm9udC1mYW1pbHk6J0x1Y2lkYSBHcmFuZGUnO2ZvbnQtc3R5bGU6bm9ybWFsO2JhY2tncm91bmQtY29sb3I6I2ZmZmZmZiI+CiAgICA8cCBzdHlsZT0iIG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiPklOU0VSVCBJTlRPIFNFTEVDVDwvcD4KICAgIDxwIHN0eWxlPSItcXQtcGFyYWdyYXBoLXR5cGU6ZW1wdHk7IG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiLz4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+VGhlIHNlY29uZCB0eXBlIG9mIElOU0VSVCBJTlRPIGFsbG93cyB1cyB0byBpbnNlcnQgbXVsdGlwbGUgcm93cyBpbnRvIGEgdGFibGUuIFVubGlrZSB0aGUgcHJldmlvdXMgZXhhbXBsZSwgd2hlcmUgd2UgaW5zZXJ0IGEgc2luZ2xlIHJvdyBieSBzcGVjaWZ5aW5nIGl0cyB2YWx1ZXMgZm9yIGFsbCBjb2x1bW5zLCB3ZSBub3cgdXNlIGEgU0VMRUNUIHN0YXRlbWVudCB0byBzcGVjaWZ5IHRoZSBkYXRhIHRoYXQgd2Ugd2FudCB0byBpbnNlcnQgaW50byB0aGUgdGFibGUuIElmIHlvdSBhcmUgdGhpbmtpbmcgd2hldGhlciB0aGlzIG1lYW5zIHRoYXQgeW91IGFyZSB1c2luZyBpbmZvcm1hdGlvbiBmcm9tIGFub3RoZXIgdGFibGUsIHlvdSBhcmUgY29ycmVjdC4gVGhlIHN5bnRheCBpcyBhcyBmb2xsb3dzOjwvcD4KICAgIDxwIHN0eWxlPSItcXQtcGFyYWdyYXBoLXR5cGU6ZW1wdHk7IG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiLz4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+SU5TRVJUIElOVE8gInRhYmxlMSIgKCJjb2x1bW4xIiwgImNvbHVtbjIiLCAuLi4pPC9wPgogICAgPHAgc3R5bGU9IiBtYXJnaW4tdG9wOjBweDsgbWFyZ2luLWJvdHRvbTowcHg7IG1hcmdpbi1sZWZ0OjBweDsgbWFyZ2luLXJpZ2h0OjBweDsgLXF0LWJsb2NrLWluZGVudDowOyB0ZXh0LWluZGVudDowcHg7Ij5TRUxFQ1QgImNvbHVtbjMiLCAiY29sdW1uNCIsIC4uLjwvcD4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+RlJPTSAidGFibGUyIjwvcD4KICAgIDxwIHN0eWxlPSItcXQtcGFyYWdyYXBoLXR5cGU6ZW1wdHk7IG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiLz4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+Tm90ZSB0aGF0IHRoaXMgaXMgdGhlIHNpbXBsZXN0IGZvcm0uIFRoZSBlbnRpcmUgc3RhdGVtZW50IGNhbiBlYXNpbHkgY29udGFpbiBXSEVSRSwgR1JPVVAgQlksIGFuZCBIQVZJTkcgY2xhdXNlcywgYXMgd2VsbCBhcyB0YWJsZSBqb2lucyBhbmQgYWxpYXNlcy48L3A+CiAgICA8cCBzdHlsZT0iLXF0LXBhcmFncmFwaC10eXBlOmVtcHR5OyBtYXJnaW4tdG9wOjBweDsgbWFyZ2luLWJvdHRvbTowcHg7IG1hcmdpbi1sZWZ0OjBweDsgbWFyZ2luLXJpZ2h0OjBweDsgLXF0LWJsb2NrLWluZGVudDowOyB0ZXh0LWluZGVudDowcHg7Ii8+CiAgICA8cCBzdHlsZT0iIG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiPlNvIGZvciBleGFtcGxlLCBpZiB3ZSB3aXNoIHRvIGhhdmUgYSB0YWJsZSwgU3RvcmVfSW5mb3JtYXRpb24sIHRoYXQgY29sbGVjdHMgdGhlIHNhbGVzIGluZm9ybWF0aW9uIGZvciB5ZWFyIDE5OTgsIGFuZCB5b3UgYWxyZWFkeSBrbm93IHRoYXQgdGhlIHNvdXJjZSBkYXRhIHJlc2lkZXMgaW4gdGhlIFNhbGVzX0luZm9ybWF0aW9uIHRhYmxlLCB3ZSdsbCB0eXBlIGluOjwvcD4KICAgIDxwIHN0eWxlPSItcXQtcGFyYWdyYXBoLXR5cGU6ZW1wdHk7IG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiLz4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+SU5TRVJUIElOVE8gU3RvcmVfSW5mb3JtYXRpb24gKHN0b3JlX25hbWUsIFNhbGVzLCBEYXRlKTwvcD4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+U0VMRUNUIHN0b3JlX25hbWUsIFNhbGVzLCBEYXRlPC9wPgogICAgPHAgc3R5bGU9IiBtYXJnaW4tdG9wOjBweDsgbWFyZ2luLWJvdHRvbTowcHg7IG1hcmdpbi1sZWZ0OjBweDsgbWFyZ2luLXJpZ2h0OjBweDsgLXF0LWJsb2NrLWluZGVudDowOyB0ZXh0LWluZGVudDowcHg7Ij5GUk9NIFNhbGVzX0luZm9ybWF0aW9uPC9wPgogICAgPHAgc3R5bGU9IiBtYXJnaW4tdG9wOjBweDsgbWFyZ2luLWJvdHRvbTowcHg7IG1hcmdpbi1sZWZ0OjBweDsgbWFyZ2luLXJpZ2h0OjBweDsgLXF0LWJsb2NrLWluZGVudDowOyB0ZXh0LWluZGVudDowcHg7Ij5XSEVSRSBZZWFyKERhdGUpID0gMTk5ODwvcD4KICAgIDxwIHN0eWxlPSItcXQtcGFyYWdyYXBoLXR5cGU6ZW1wdHk7IG1hcmdpbi10b3A6MHB4OyBtYXJnaW4tYm90dG9tOjBweDsgbWFyZ2luLWxlZnQ6MHB4OyBtYXJnaW4tcmlnaHQ6MHB4OyAtcXQtYmxvY2staW5kZW50OjA7IHRleHQtaW5kZW50OjBweDsiLz4KICAgIDxwIHN0eWxlPSIgbWFyZ2luLXRvcDowcHg7IG1hcmdpbi1ib3R0b206MHB4OyBtYXJnaW4tbGVmdDowcHg7IG1hcmdpbi1yaWdodDowcHg7IC1xdC1ibG9jay1pbmRlbnQ6MDsgdGV4dC1pbmRlbnQ6MHB4OyI+SGVyZSBJIGhhdmUgdXNlZCB0aGUgU1FMIFNlcnZlciBzeW50YXggdG8gZXh0cmFjdCB0aGUgeWVhciBpbmZvcm1hdGlvbiBvdXQgb2YgYSBkYXRlLiBPdGhlciByZWxhdGlvbmFsIGRhdGFiYXNlcyB3aWxsIGhhdmUgZGlmZmVyZW50IHN5bnRheC4gRm9yIGV4YW1wbGUsIGluIE9yYWNsZSwgeW91IHdpbGwgdXNlIHRvX2NoYXIoZGF0ZSwneXl5eScpPTE5OTguPC9wPgogIDwvYm9keT4KPC9odG1sPg=='))
    
    app.exec_()
    
    print rtfEditor.data()
    print '-'*50
    print '\n'.join(rtfEditor.data().split('\n')[:5])
    print '-'*50
    print '\n'.join(rtfEditor.data().split('\n')[-2:])
    print '-'*50
    print '\n'.join(rtfEditor.data().split('\n')[5:-2])
    
    
## ----------------------------------------------------------------------------
if __name__ == '__main__':
    main() 